package com.wxapp.common.core.annotation.resolver;

import com.wxapp.common.base.util.redis.RedisUtil;
import com.wxapp.common.constant.constant.CatchCommonConstant;
import com.wxapp.common.constant.constant.LoginConstant;
import com.wxapp.common.constant.enums.ResultEnum;
import com.wxapp.common.core.annotation.AccessLimit;
import com.wxapp.common.core.filter.except.ThrowMyException;
import com.wxapp.common.core.annotation.util.SpELUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author lgl
 * @Description
 * @Datetime 2023/6/29 9:45
 * @Modified By
 */
@Component
@Aspect
@RequiredArgsConstructor
public class AccessLimitResolver {
    private final RedisUtil redisUtil;

    @Pointcut("@annotation(com.wxapp.common.core.annotation.AccessLimit)")
    private void pointCut() {
    }

    /**
     * 在切入环绕通知
     */
    @Around(value = "pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        Method method = getCatchMethod(joinPoint);
        var annotation = method.getAnnotation(AccessLimit.class);
        var name = annotation.name();
        var args = joinPoint.getArgs();
        if (name.contains("#")) {
            name = SpELUtil.parseExpression(name, method, args);
        }
        String key = CatchCommonConstant.REQUEST_LIMIT + method.getName()+ ":" + name;
        if (annotation.isLogin()) {
            key = key + ":" + request.getHeader(LoginConstant.INTERNAL_TOKEN_KEY);
        }
        var countValue = redisUtil.get(key);
        int count = 0;
        if (countValue != null) {
            count = Integer.parseInt(String.valueOf(countValue));
        }
        if (count > annotation.maxCount()) {
            throw new ThrowMyException(ResultEnum.FREQUENT_REQUESTS);
        }
        redisUtil.set(key, count + 1, annotation.seconds());
        //先执行方法 在删除缓存
        return joinPoint.proceed(args);
    }

    private Method getCatchMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        return ((MethodSignature) joinPoint.getSignature()).getMethod();
    }

}
